[TS 杂谈](1)Promise.all 优雅的类型声明 您所在的位置:网站首页 typescript 元组 [TS 杂谈](1)Promise.all 优雅的类型声明

[TS 杂谈](1)Promise.all 优雅的类型声明

2023-10-07 05:39| 来源: 网络整理| 查看: 265

前言

TS 内置的Promise.all,在lib.es2015.promise.d.ts文件中声明,通过函数重载定义多个泛型进行类型声明的。

而在最新的 TS(4.1.3) 中已经有比较优雅的方法进行声明了,因此这篇文章的作用就是介绍怎么写出比较优雅一个Promise.all类型。(不包括函数实现)

前置知识 as const 声明元组

在某个版本以前,声明元组只能通过[string, typeof X, number]一个个手动声明,而现在可以通过as const进行声明元组,用法如下:

const tuple = ['你好', '元组', 17] as const // ^^^^^ = readonly ["你好", "元组", 17]

可以看到这样就声明了一个元组,之前的话就得一个个写元组元素声明。

映射元组

假设依旧有上面的tuple变量,现在有个需求需要把tuple变量的每个元素都转成Promise类型,而这时候就需要使用映射元组的技巧了,语法和映射类型一致。

type TuplePromise = { [K in keyof T]: Promise } type T1 = TuplePromise // ^^ = readonly [Promise, Promise, Promise] 类型实现

假设之后都有如下六个类型

const ajax1: Promise = Promise.resolve(':)') const ajax2: Promise = Promise.resolve(17) const ajax3: Promise = Promise.resolve(true) const ajax4: string = ':)' const ajax5: number = 17 const ajax6: boolean = true const ajaxArr = [ajax1, ajax2, ajax3, ajax4, ajax5, ajax6] as const 原生 Promise.all 类型

在lib.es2015.promise.d.ts文件中可以找到对应的函数声明,建议通过 VSC 编辑器中使用ctrl+鼠标左键Promise.all跳转定义,定义如下图。

如图所示

可以看到源码类型是通过使用泛型T进行类型声明的,源码中最多参数只能有10个,因为定义的重载只有10个,最后一个就是T1-T10,所以当参数超过十个的时候就会报错。(虽然不会有这个场景)

Promise.all行为,因为使用的泛型,因此可以不用传入元组,传入数组也能识别。

Promise.all([ajax1, ajax2, ajax3, ajax4, ajax5, ajax6]) // 这是运行时类型 Promise.all([Promise, Promise, Promise, string, number, boolean]) // 返回 Promise .then(res => {}) // res: [string, number, boolean, string, number, boolean]

可以看到是Promise的话就会拆出里面.then参数的类型,如果不是则原样返回。通过源码,我们可以看出是用PromiseLike的类型来进行拆解的,这是因为Promise.all可以使用含有.then的对象。

因此只要含有.then方法,就要拆出方法参数的类型。

myPromiseAll 类型实现

myPromiseAll类型只能接受一个元组参数,然后通过元组映射进行拆解,最后返回Promise。

由上一节可以得出我们需要一个类型来提取.then的方法参数类型,这个很简单,可以使用内置的PromiseLike类型判断是否含有.then方法且还会自动获取方法参数类型,因此通过infer可以轻松取出来。

type GetPromiseLikeThenParam = T extends PromiseLike ? U : T type GPLTP = GetPromiseLikeThenParam // 测试 type T1 = GPLTP // ^^ = string type T2 = GPLTP // ^^ = string

映射元组类型进行提取元组每一个PromieLike类型。

type ExtractTuplePromiseLike = { [K in keyof T]: GPLTP } type ETPL = ExtractTuplePromiseLike type T1 = ETPL

ReadonlyArray 相当于 readonly unknown[]。

基础类型准备就绪,接下来就是写函数声明。

函数参数是一个元组,因此声明参数为ReadonlyArray,由于返回的类型与函数参数有关,因此函数参数要声明为泛型T,然后返回就是通过上面的ETPL提取T,然后再用Promise包装就成功写好myPromiseAll函数类型

declare function myPromiseAll( tuple: T, ): Promise // 测试 myPromiseAll(ajaxArr) .then((res) => {}) // ^^^ = readonly [string, number, boolean, string, number, true]

和Promise.all的区别是多了个readonly和变量使用时需要用到as const,如果为了方便可以这么写:

myPromiseAll([ajax1, ajax2, ajax3, ajax4, ajax5, ajax6] as const).then((res) => {})

也是完全没有问题。

总结

myPromiseAll相较于Promise.all的类型还是有些区别的,Promise.all在数组长度超过10的时候会报错而myPromiseAll不会。

myPromiseAll需要通过as const进行参数声明传入元组,Promise.all不需要。

myPromiseAll返回的Promise



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有